#include <stdio.h>
#include <malloc.h>
#include <string.h>

#define MAXR        10
#define MAX_SIZE    10

typedef struct node
{
    char xm[10];                            //  姓名
    char xb;                                //  性别  m:男 f:女
    char bh[6];                             //  班号
    struct node *next;                      //  指向下一个结点的指针
}stud_node;                                 //  学生单链表结点类型

typedef struct
{
    char xm[10];                            //  姓名
    char xb;                                //  性别  m:男 f:女
    char bh[6];                             //  班号
}stud_type;                                 //  学生记录类型

/**
*   功能:
*       由学生记录数组students创建单链表p
*
*/
static void create_link(stud_node *&p, stud_type students[], int n)     //  指针的引用
{
    int i;
    stud_node *s, *t;

    p = NULL;
    for(i = 0; i < n; i++)
    {
        s = (stud_node *)malloc(sizeof(stud_node));                     //  动态分配存储空间(新创建的结点)
        strcpy(s->xm, students[i].xm);
        s->xb = students[i].xb;
        strcpy(s->bh, students[i].bh);
        if(p == NULL)
        {
            p = s;                                                      //  p和t指向新创建的结点
            t = s;
        }
        else
        {
            t->next = s;                                                //  采用尾插法建立单链表
            t = s;                                                      //  t指向新创建的结点
        }
    }
    t->next = NULL;                                                     //  尾结点的next域置为NULL
}

/*------------输出学生单链表p--------------*/
static void disp_link(stud_node *p)
{
    int i = 0;

    while(p != NULL)
    {
        printf(" %s(%s,%c)", p->xm, p->bh, p->xb);
        p = p->next;
        if((i + 1) % 5 == 0)            // 显示5个数据为一行
            printf("\n");
        i++;
    }
    printf("\n");
}

/*------------销毁学生单链表p--------------*/
static void destroy_link(stud_node *p)
{
    stud_node *pre = p, *q = pre->next;

    while(q != NULL)
    {
        free(pre);
        pre = q;
        q = q->next;
    }
    free(pre);
}

/**
*   功能:
*       对性别进行排序,只需进行一趟
*   @param: p为待排序序列链表指针
*   @param: r为基数
*   @param: d为关键字位数
*/
static void sort_by_xb(stud_node *&p, int r, int d)     //  指针的引用
{
    stud_node *head[MAXR], *tail[MAXR], *t;             //  定义各链队的首尾指针
    int j;                                              //  循环变量
    int k;                                              //  区分链队    女:k=0,男:k=1

    printf("按性别排序\n");
    for(j = 0; j < r; j++)                              //  初始化各链队首、尾指针
    {
        head[j] = tail[j] = NULL;
    }

    while(p != NULL)                                    //  对于原链表中每个结点循环
    {
        if(p->xb == 'f')                                //  找第k个链队
        {
            k = 0;
        }
        else
        {
            k = 1;
        }

        if(head[k] == NULL)                             //  进行分配,即采用尾插法建立单链表
        {
            head[k] = p;
            tail[k] = p;
        }
        else
        {
            tail[k]->next = p;
            tail[k] = p;
        }
        p = p->next;                                    //  取下一个待排序的元素
    }

    p = NULL;
    for(j = 0; j < r; j++)                              //  对每一个链队循环
    {
        if(head[j] != NULL)
        {
            if(p == NULL)
            {
                p = head[j];
                t = head[j];
            }
            else
            {
                t->next = head[j];
                t = tail[j];
            }
        }
    }
    t->next = NULL;                                     //  最后一个结点的next域置为NULL
    disp_link(p);                                       //  输出单链表
}

/**
*   功能:
*       对班号进行排序
*   @param: p为待排序序列链表指针
*   @param: r为基数
*   @param: d为关键字位数
*/
static void sort_by_bh(stud_node *&p, int r, int d)         //  指针的引用
{
    stud_node *head[MAXR], *tail[MAXR], *t;                 //  定义各链队的首尾指针
    int i, j;                                               //  循环变量
    int k;                                                  //  k区分链队

    printf("按班号排序\n");
    for(i = 3; i >= 2; i--)                                 //  从低位到高位做d趟排序
    {
        for(j = 0; j < r; j++)                              //  初始化各链队首尾指针
        {
            head[j] = tail[j] = NULL;
        }
        while(p != NULL)                                    //  对于原链表中每个结点循环
        {
            k = p->bh[i] - '0';                             //  找第k个链队
            if(head[k] == NULL)                             //  进行分配,即采用尾插法建立单链表
            {
                head[k] = p;
                tail[k] = p;
            }
            else
            {
                tail[k]->next = p;
                tail[k] = p;
            }

            p = p->next;                                    //  取下一个待排序的元素
        }
        p = NULL;
        for(j = 0; j < r; j++)                              //  对每一个链队循环
        {
            if(head[j] != NULL)
            {
                if(p == NULL)
                {
                    p = head[j];
                    t = tail[j];
                }
                else
                {
                    t->next = head[j];
                    t = tail[j];
                }
            }
        }
        t->next = NULL;                                     //  最后一个结点的next域置为NULL
        printf(" 第%d趟:\n", d - i + 2);
        disp_link(p);                                       //  输出单链表
    }
}

/**
*   功能:
*       按班号和性别排序
*       在班号和性别中，班号优先，性别次之，所以先按
*   性别排序，然后再按班号排序。由于班号4位中只有后
*   两位不同，故d=2，从低位到高位排序。
*/
static void sort_stus(stud_type students[], int n)
{
    stud_node *p;

    create_link(p, students, n);
    printf("排序前:\n");
    disp_link(p);
    sort_by_xb(p, 2, 1);                    //  按照性别排序
    sort_by_bh(p, 10, 2);                   //  按照班号的后两位排序
    printf("排序后:\n");
    disp_link(p);
    destroy_link(p);
}

int main(void)
{
    int n = 10;
    //  定义结构体数组并初始化
    stud_type students[MAX_SIZE] = {
        {"王华", 'm', "1003"}, {"陈兵", 'm', "1020"},
        {"许可", 'f', "1022"}, {"李英", 'f', "1003"},
        {"张冠", 'm', "1021"}, {"陈强", 'm', "1002"},
        {"李真", 'f', "1002"}, {"章华", 'm', "1001"},
        {"刘丽", 'f', "1021"}, {"王强", 'm', "1022"},
    };

    sort_stus(students, n);

    return 0;
}
 
